#include "common\GLApp.h"


// entry point
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	int ret=-1;
	GL_Application* appl=GL_Application::create("Open GL");
	if(appl!=0)
	{
		ret=appl->Main(hInstance,hPrevInstance,lpCmdLine,nCmdShow);
		delete appl;
	}
	else
	{
		MessageBox(HWND_DESKTOP,"Error creating application.","Error",MB_OK|MB_ICONEXCLAMATION);
	}
	return ret;
}

// process window message callbacks
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LONG userData=GetWindowLong(hWnd,GWL_USERDATA);
	if(userData==0)
	{
		if(uMsg==WM_CREATE)
		{
			CREATESTRUCT* creation=reinterpret_cast<CREATESTRUCT*>(lParam);
			GL_Application* appl=reinterpret_cast<GL_Application*>(creation->lpCreateParams);
			SetWindowLong(hWnd,GWL_USERDATA,reinterpret_cast<LONG>(appl));
			appl->m_isVisible=true;
			return 0;
		}
	}
	else
	{
		GL_Application* appl=reinterpret_cast<GL_Application*>(userData);
		return appl->Message(hWnd,uMsg,wParam,lParam);
	}
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

// default values
GL_Application::GL_Application(const char* className)
{
	m_className=className;
	m_isProgramLooping=true;
	m_createFullScreen=true;
	m_isVisible=false;
	m_resizeDraw=false;
	m_lastTickCount=0;
}

void GL_Application::toggleFullScreen()
{
	PostMessage(m_window,WM_TOGGLEFULLSCREEN,0,0);
}

void GL_Application::terminateApplication()
{
	PostMessage(m_window,WM_QUIT,0,0);
	m_isProgramLooping=false;
}

// message handler
LRESULT GL_Application::Message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg)
	{
	case WM_SYSCOMMAND:
		switch(wParam)
		{
			// prevent screensaver or powersaving
		case SC_SCREENSAVE:
		case SC_MONITORPOWER:
			return 0;
			break;
		}
		break;
	case WM_CLOSE:
		terminateApplication();
		return 0;
		break;
	case WM_EXITMENULOOP:
	case WM_EXITSIZEMOVE:
		m_lastTickCount=GetTickCount();
		return 0;
		break;
	case WM_MOVE:
		m_window.setPosX(LOWORD(lParam));
		m_window.setPosY(HIWORD(lParam));
		return 0;
		break;
	case WM_PAINT:
		if(m_resizeDraw)
		{
			m_window.reshapeGL();
			draw();
			m_window.swapBuffers();
		}
		break;
	case WM_SIZING:
		{
			RECT* rect=(RECT*)lParam;
			m_window.setWidth(rect->right-rect->left);
			m_window.setHeight(rect->bottom-rect->top);
			return TRUE;
		}
		break;
	case WM_SIZE:
		switch(wParam)
		{
		case SIZE_MINIMIZED:
			m_isVisible=false;
			return 0;
			break;
		case SIZE_MAXIMIZED:
		case SIZE_RESTORED:
			m_isVisible=true;
			m_window.setWidth(LOWORD(lParam));
			m_window.setHeight(HIWORD(lParam));
			m_window.reshapeGL();
			m_lastTickCount=GetTickCount();
			return 0;
			break;
		}
		break;
	case WM_KEYDOWN:
		m_keys.setPressed(wParam);
		return 0;
		break;
	case WM_KEYUP:
		m_keys.setReleased(wParam);
		return 0;
		break;
  case WM_MOUSEMOVE:
    m_mouse.setXY(LOWORD(lParam), HIWORD(lParam));
    return 0;
    break;
case WM_LBUTTONDOWN:
    m_mouse.setPressed(0);
    m_mouse.setClick(0, LOWORD(lParam), HIWORD(lParam));
    return 0;
    break;
case WM_LBUTTONUP:
    m_mouse.setReleased(0);
    m_mouse.doneClick(0);
    return 0;
    break;
case WM_RBUTTONDOWN:
    m_mouse.setPressed(0);
    m_mouse.setClick(0, LOWORD(lParam), HIWORD(lParam));
    return 0;
    break;
case WM_RBUTTONUP:
    m_mouse.setReleased(0);
    m_mouse.doneClick(0);
    return 0;
    break;
	case WM_TOGGLEFULLSCREEN:
		m_createFullScreen=!m_createFullScreen;
		PostMessage(hWnd,WM_QUIT,0,0);
		break;
	}
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

// main loop
int GL_Application::Main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX windowClass;
	ZeroMemory(&windowClass,sizeof(WNDCLASSEX));
	windowClass.cbSize=sizeof(WNDCLASSEX);
	windowClass.style=CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
	windowClass.lpfnWndProc=(WNDPROC)(WindowProc);
	windowClass.hInstance=hInstance;
	windowClass.hbrBackground=(HBRUSH)(COLOR_APPWORKSPACE);
	windowClass.hCursor=LoadCursor(NULL,IDC_ARROW);
	windowClass.lpszClassName=m_className;
	if(RegisterClassEx(&windowClass)==0)
	{
		MessageBox(HWND_DESKTOP,"Error registering window class.","Error",MB_OK|MB_ICONEXCLAMATION);
		return -1;
	}
	if(MessageBox(HWND_DESKTOP,"Start in full screen mode?","Full screen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
	{
		m_createFullScreen=false;
	}
	while(m_isProgramLooping)
	{
		if(m_window.create("Open GL Base",m_createFullScreen,m_className,hInstance,this))
		{
			if(initialize()==false) terminateApplication();
			else 
			{
				MSG msg;
				bool isMessagePumpActive=true;
				m_lastTickCount=GetTickCount();
				m_keys.clear();
				while(isMessagePumpActive)
				{
					if(PeekMessage(&msg,m_window,0,0,PM_REMOVE)!=0)
					{
						if(msg.message!=WM_QUIT) DispatchMessage(&msg);
						else isMessagePumpActive=false;
					}
					else
					{
						if(m_isVisible==false) WaitMessage();
						else
						{							
							DWORD tickCount=GetTickCount();																												
							update(tickCount-m_lastTickCount);							
							m_lastTickCount=tickCount;								
							draw();
							m_window.swapBuffers();							
						}
					}
				}
			}
			deinitialize();
			m_window.destroy();
		}
		else
		{
			MessageBox(HWND_DESKTOP,"Error creating OpenGL window.","Error",MB_OK|MB_ICONEXCLAMATION);
			m_isProgramLooping=false;
		}
	}
	UnregisterClass(m_className,hInstance);
	return 0;
}